////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNMemory/inc/WNMemory.h"
#include "WNStrings/inc/WNStrings.h"
#include "WNScripting/inc/WNExpression.h"
#include "WNScripting/inc/WNExpressionList.h"
#include "WNScripting/inc/WNFunctionManager.h"
#include "WNScripting/inc/WNLLVMHelpers.h"

#include <llvm/Intrinsics.h>

using namespace WNStrings;
using namespace WNMemory;
using namespace WNScripting;

#ifdef _WN_MSVC
    #pragma warning(push)
    #pragma warning(disable: 4996)  // Function was declared deprecated
#endif

WNExpression::WNExpression(WN_SIZE_T _startLine, 
                           WN_SIZE_T _endLine, 
                           WN_SIZE_T _startChar, 
                           WN_SIZE_T _endChar,
                           WNIdentifierElement _identifier):
       mElementType(WNInvalidType),
       WNParseNode(_startLine, _endLine, _startChar, _endChar),
       mFolded(WN_FALSE),
       mPredicate(eWNInvalidPredicate),
       mSuppressLastCreation(WN_FALSE) {
    mFirstElement.mIdentifier = _identifier;
    mType = eWNIdentifierExpression;
}

WNExpression::WNExpression(WN_SIZE_T _startLine, 
                           WN_SIZE_T _endLine, 
                           WN_SIZE_T _startChar, 
                           WN_SIZE_T _endChar,
                           WNS_INT_CONST  _constVal,
                           WNExpressionConstantInt):
       mElementType(WNInvalidType),
       WNParseNode(_startLine, _endLine, _startChar, _endChar),
       mFolded(WN_FALSE),
       mPredicate(eWNInvalidPredicate),
       mSuppressLastCreation(WN_FALSE) {
    *((WNS_INT_CONST*)(&mFirstElement.mConstVal[0])) = _constVal;
    mType = eWNConstantExpression;
    mConstType = eWNConstantInt;
}

WNExpression::WNExpression(WN_SIZE_T _startLine,
                           WN_SIZE_T _endLine,
                           WN_SIZE_T _startChar,
                           WN_SIZE_T _endChar,
                           WNS_CHAR_CONST _constChar,
                           WNExpressionConstantChar):
        mElementType(WNInvalidType),
        WNParseNode(_startLine, _endLine, _startChar, _endChar),
        mFolded(WN_FALSE),
        mPredicate(eWNInvalidPredicate),
        mSuppressLastCreation(WN_FALSE) {
    *((WNS_CHAR_CONST*)(&mFirstElement.mConstVal[0])) = _constChar;
    mType = eWNConstantExpression;
    mConstType = eWNConstantChar;
}


WNExpression::WNExpression(WN_SIZE_T _startLine, 
                           WN_SIZE_T _endLine, 
                           WN_SIZE_T _startChar, 
                           WN_SIZE_T _endChar,
                           WNS_FLOAT_CONST  _constVal,
                           WNExpressionConstantFloat):
       mElementType(WNInvalidType),
       WNParseNode(_startLine, _endLine, _startChar, _endChar),
       mFolded(WN_FALSE),
       mPredicate(eWNInvalidPredicate),
       mSuppressLastCreation(WN_FALSE) {
     *((WNS_FLOAT_CONST*)(&mFirstElement.mConstVal[0])) = _constVal;
    mType = eWNConstantExpression;
    mConstType = eWNConstantFloat;
}

WNExpression::WNExpression(WN_SIZE_T _startLine,
                           WN_SIZE_T _endLine,
                           WN_SIZE_T _startChar,
                           WN_SIZE_T _endChar,
                           WNExpressionType _type,
                           WNExpression* _LHS,
                           WNExpression* _RHS):
       WNParseNode(_startLine, _endLine, _startChar, _endChar),
       mElementType(WNInvalidType),
       mFolded(WN_FALSE),
       mPredicate(eWNInvalidPredicate),
       mSuppressLastCreation(WN_FALSE) {
    mFirstElement.mLHS = _LHS;
    mSecondElement.mRHS = _RHS;
    mType = _type;
}

WNExpression::WNExpression(WN_SIZE_T _startLine,
                           WN_SIZE_T _endLine,
                           WN_SIZE_T _startChar,
                           WN_SIZE_T _endChar,
                           WNExpression* _LHS,
                           WNExpression* _RHS,
                           WNStaticPredicate _pred):
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mFolded(WN_FALSE),
    mElementType(WNInvalidType),
    mSuppressLastCreation(WN_FALSE) {
        mFirstElement.mLHS = _LHS;
        mSecondElement.mRHS = _RHS;
        mType = eWNArithmeticEquality;
        mPredicate = _pred;
}


WNExpression::WNExpression(WN_SIZE_T _startLine,
                           WN_SIZE_T _endLine,
                           WN_SIZE_T _startChar,
                           WN_SIZE_T _endChar,
                           WNDataType* _dataType,
                           WN_BOOL _heapAlloc):
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mElementType(WNInvalidType),
    mSuppressLastCreation(WN_FALSE) {
        mFirstElement.mDataInitializer = _dataType;
        mType = eWNReferenceTypeInitialization;
        mHeapAlloc = _heapAlloc;
}

WNExpression::WNExpression(WN_SIZE_T _startLine,
        WN_SIZE_T _endLine,
        WN_SIZE_T _startChar,
        WN_SIZE_T _endChar,
        WNS_STRING_CONST _dataType,
        WN_BOOL _heapAlloc):
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mElementType(WNInvalidType),
    mSuppressLastCreation(WN_FALSE) {
        mFirstElement.mStringInitializer = _dataType;
        mType = eWNConstantStringInitialization;
        mHeapAlloc = _heapAlloc;
    }

WNExpression::WNExpression(WN_SIZE_T _startLine, 
                           WN_SIZE_T _endLine, 
                           WN_SIZE_T _startChar, 
                           WN_SIZE_T _endChar,
                           WNIdentifierElement _identifier,
                           WNExpressionList* _expressionList):
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mElementType(WNInvalidType),
    mFolded(WN_FALSE),
    mPredicate(eWNInvalidPredicate),
    mSuppressLastCreation(WN_FALSE) {
    mFirstElement.mIdentifier = _identifier;
    mSecondElement.mExpressionList = _expressionList;
    mType = eWNFunctionCall;
}

WNExpression::WNExpression(WN_SIZE_T _startLine,
                           WN_SIZE_T _endLine,
                           WN_SIZE_T _startChar,
                           WN_SIZE_T _endChar,
                           WNExpression* _LHS,
                           WNIdentifierElement _structIdent) :
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mElementType(WNInvalidType),
    mFolded(WN_FALSE),
    mPredicate(eWNInvalidPredicate),
    mType(eWNStructAccess),
    mSuppressLastCreation(WN_FALSE) {
    mFirstElement.mLHS = _LHS;
    mSecondElement.mIdentifier = _structIdent;
}

WNExpression::WNExpression (WN_SIZE_T _startLine,
                            WN_SIZE_T _endLine,
                            WN_SIZE_T _startChar,
                            WN_SIZE_T _endChar,
                            WNDataType* _dataType,
                            WN_BOOL _heap,
                            std::vector<WNExpression*>* _exprList,
                            WN_BOOL _ignoreLastCreation):
    WNParseNode(_startLine, _endLine, _startChar, _endChar),
    mHeapAlloc(_heap),
    mSuppressLastCreation(_ignoreLastCreation) {
    mFirstElement.mDataInitializer = _dataType;
    mSecondElement.mArrayExpressions = _exprList;
    mType = eWNReferenceCreation;
}

WNExpression::WNExpression(WN_SIZE_T _startLine,
                WN_SIZE_T _endLine,
                WN_SIZE_T _startChar,
                WN_SIZE_T _endChar,
                WNExpression* _expr,
                WNExpressionType _type) :
    WNParseNode(_startLine, _endLine, _startChar, _endChar) {
    mFirstElement.mLHS = _expr;

    WN_DEBUG_ASSERT(_type == eWNArrayLength);
    mType = _type;
};

WNExpression::~WNExpression() {
    switch(mType){
        case eWNArithmeticAdd:
        case eWNArithmeticSubtract:
        case eWNArithmeticMultiply:
        case eWNArithmeticDivide:
        case eWNArithmeticMod:
        case eWNArithmeticSquareBrackets:
            WN_DELETE(mFirstElement.mLHS);
            WN_DELETE(mSecondElement.mRHS);
            break;
        case eWNIdentifierExpression:
            break;
        case eWNReferenceTypeInitialization:
            WN_DELETE(mFirstElement.mDataInitializer);
            break;
        case eWNReferenceCreation:
            WN_DELETE(mFirstElement.mDataInitializer);
            WN_DELETE(mSecondElement.mArrayExpressions);
        case eWNConstantStringInitialization:
            WN_DELETE(mFirstElement.mDataInitializer);
            break;
        case eWNFunctionCall:
            WN_DELETE(mSecondElement.mExpressionList);
        case eWNArrayLength:
            WN_DELETE(mFirstElement.mLHS);
    }
}

WN_BOOL WNExpression::ConstantFold(WNScriptingEngineData& _scriptingData) {
    //This will prevent our constant folding from happening more than once 
    //   (which would be unfortunate in terms of useless computation)
    if (mFolded == WN_TRUE) {
        return(WN_TRUE);
    }

    mFolded = WN_TRUE;

    switch(mType) {
        case eWNArithmeticAdd:
        case eWNArithmeticSubtract:
        case eWNArithmeticMultiply:
        case eWNArithmeticMod:
        case eWNArithmeticDivide:
        case eWNArithmeticSquareBrackets: {
            mFirstElement.mLHS->ConstantFold(_scriptingData);
            mSecondElement.mRHS->ConstantFold(_scriptingData);
            WNStaticArithmetic arithType = eWNStaticERROR;

            switch(mType) {
                case eWNArithmeticAdd:
                    arithType = eWNStaticAdd;
                    break;
                case eWNArithmeticDivide:
                    arithType = eWNStaticDivide;
                    break;
                case eWNArithmeticMultiply:
                    arithType = eWNStaticMultiply;
                    break;
                case eWNArithmeticMod:
                    arithType = eWNStaticMod;
                case eWNArithmeticSubtract:
                    arithType = eWNStaticSubtract;
                    break;
                default:
                    arithType = eWNStaticERROR;
            }

            if (mFirstElement.mLHS->mType == eWNConstantExpression && //Do manual Constant Folding and all that jazz!
                mSecondElement.mRHS->mType == eWNConstantExpression) {
                WN_CHAR cVal[WN_SCRIPTING_MAX_CONST_SIZE];
                WN_DEBUG_ASSERT(arithType != eWNStaticERROR);
                WNCastAllowance allow = eWNCastFail;
                WNTypeElement type = WN_NULL;
                _scriptingData.mTypeManager->GetStaticArithmeticFunction(mFirstElement.mLHS->GetType(), mSecondElement.mRHS->GetType())(mFirstElement.mLHS->mFirstElement.mConstVal,
                                                                                                                            mSecondElement.mRHS->mFirstElement.mConstVal,
                                                                                                                            arithType, mPredicate, type, allow, cVal);
                if (allow == eWNCastFail) {
                    mFail = "Error detected in casting";
                    return(WN_FALSE);
                } else if (allow == eWNCastInvalidArithmetic) {
                    mFail = "Error invalid arithmetic operator";
                    return(WN_FALSE);
                } else if (allow == eWNCastLossPrecision) {
                    mWarning = "Warning, loss of data detected";
                }

                if (!_scriptingData.mTypeManager->GetStaticTypeCheckFunction(mFirstElement.mLHS->GetType())(cVal)) {
                    mFail = "Error detected in expression parsing, constants are invalid";
                    return(WN_FALSE);
                }
               
                mElementType = type;
                WN_DELETE(mFirstElement.mLHS);
                WN_DELETE(mSecondElement.mRHS);
                mSecondElement.mRHS = WN_NULL;
                WNMemCpy(mFirstElement.mConstVal, cVal, WN_SCRIPTING_MAX_CONST_SIZE * sizeof(WN_CHAR));
                mType = eWNConstantExpression;
            }
            break;
        }
        case eWNArithmeticEquality:
            mElementType = _scriptingData.mTypeManager->GetType(eWNTypeBool);
            break;
        case eWNConstantExpression: {
                if (mConstType == eWNConstantFloat) {
                    mElementType = _scriptingData.mTypeManager->GetType(eWNTypeFloat);
                } else if (mConstType == eWNConstantInt) {
                    mElementType = _scriptingData.mTypeManager->GetType(eWNTypeInt);
                } else if (mConstType == eWNConstantChar) {
                    mElementType = _scriptingData.mTypeManager->GetType(eWNTypeChar);
                }
            }
            break;
        case eWNIdentifierExpression: {
                const WNScriptVariable& var = _scriptingData.mScopedVariableList->GetVariable(mFirstElement.mIdentifier);
                if (!WNIsValid(var)){
                    mFail = "Variable does not exist";
                    return(WN_FALSE);
                }
            }
            break;
        case eWNStructAccess: {
                mFirstElement.mLHS->ConstantFold(_scriptingData);
            }
            break;
        case eWNReferenceCreation: {
                for(WN_SIZE_T i = 0; i < mSecondElement.mArrayExpressions->size(); ++i){
                    if(!(*mSecondElement.mArrayExpressions)[i]->ConstantFold(_scriptingData)){
                        return(WN_FALSE);
                    }
                }
            }
            break;
        case eWNConstantStringInitialization: {
                break;
            }
        case eWNReferenceTypeInitialization:
        case eWNFunctionCall:
        case eWNArrayLength:
            break;
    }
    return(WN_TRUE);
}


llvm::Value* WNExpression::OptimizeAndGenerate(WNScriptingEngineData& _scriptingData, WNFunctionDecl* _functionDecl) {
    llvm::IRBuilder<>* builder = reinterpret_cast<llvm::IRBuilder<>* >(_scriptingData.mBuilder);
    WNParseNode::OptimizeAndGenerate(_scriptingData, _functionDecl);
    if (ConstantFold(_scriptingData) == WN_FALSE) {
        return(WN_NULL);
    }
    switch(mType) {
        case eWNArithmeticSquareBrackets:
        case eWNArithmeticAdd:
        case eWNArithmeticSubtract:
        case eWNArithmeticMultiply:
        case eWNArithmeticMod:
        case eWNArithmeticDivide:
        case eWNArithmeticEquality: {
            WNStaticArithmetic arithType = eWNStaticERROR;
            switch(mType){ //MAYBE? use a goto here, would be a bit cleaner (less duplication)
                case eWNArithmeticAdd:
                    arithType = eWNStaticAdd;
                    break;
                case eWNArithmeticDivide:
                    arithType = eWNStaticDivide;
                    break;
                case eWNArithmeticMultiply:
                    arithType = eWNStaticMultiply;
                    break;
                case eWNArithmeticMod:
                    arithType = eWNStaticMod;
                    break;
                case eWNArithmeticSubtract:
                    arithType = eWNStaticSubtract;
                    break;
                case eWNArithmeticEquality:
                    arithType = eWNStaticEquality;
                    break;
                case eWNArithmeticSquareBrackets:
                    arithType = eWNStaticBrackets;
                    break;
                default:
                    arithType = eWNStaticERROR;
                    break;
            }
            llvm::Value* LHS = mFirstElement.mLHS->OptimizeAndGenerate(_scriptingData, _functionDecl);
            llvm::Value* RHS = mSecondElement.mRHS->OptimizeAndGenerate(_scriptingData, _functionDecl);
           
            WNTypeElement type = WN_NULL;
            WNCastAllowance allow = _scriptingData.mTypeManager->GetDynamicArithmeticVerification(mFirstElement.mLHS->GetType(), mSecondElement.mRHS->GetType())(arithType, mFirstElement.mLHS->GetType(), mSecondElement.mRHS->GetType(), type);
            
            if (allow == eWNCastFail) {
                mFail = "Cannot cast expression to variable type";
                return(WN_NULL);
            } else if (allow == eWNCastInvalidArithmetic) {
                mFail = "Error invalid arithmetic operator";
                return(WN_NULL);
            } else if (allow == eWNCastLossPrecision) {
                mWarning = "The cast can proceed, but there will be loss of precision";
            }
            WNGenerateLLVMOperation opFunc = _scriptingData.mTypeManager->GetLLVMOperationGenerator(
                                                mFirstElement.mLHS->GetType(), 
                                                mSecondElement.mRHS->GetType());
            if (!opFunc) {
                mFail = "Error this operation was is not valid for these types";
            }
            mElementType = type;
            return(opFunc(LHS, RHS, mFirstElement.mLHS->GetType(), arithType, mPredicate, _scriptingData));
        }
            break;
        case eWNConstantExpression: {
            WNCreateConstantFunction func = _scriptingData.mTypeManager->GetLLVMCreateConstant(mElementType);
            return(func(_scriptingData, mFirstElement.mConstVal));
        }
            break;
        case eWNIdentifierExpression: {
            const WNScriptVariable& var = _scriptingData.mScopedVariableList->GetVariable(mFirstElement.mIdentifier);
            if (!WNIsValid(var)) {
                mFail = "Variable does not exist";
                return(WN_NULL);
            }
            mElementType = var.mType;

            return(LLVMHelpCreateLoad(*builder, var));
            
        }
        case eWNReferenceTypeInitialization: {
            mFirstElement.mDataInitializer->OptimizeAndGenerate(_scriptingData, _functionDecl);
            llvm::Value* val = WNLLVMHelpCreateArrayAlloca(mFirstElement.mDataInitializer, _scriptingData, _functionDecl, mHeapAlloc, mFirstElement.mDataInitializer->GetSubTypeArray()->begin(), mFirstElement.mDataInitializer->GetSubTypeArray()->end());
            if (val == WN_NULL) {
                mFail = "Invalid data initialization";
                return(WN_NULL);
            }

            mElementType = mFirstElement.mDataInitializer->GetType();
            return(val);
        }
        break;
        case eWNFunctionCall: {
            std::vector<WNExpressionList::WNExpressionListVal> parameters;
            if (mSecondElement.mExpressionList){
                mSecondElement.mExpressionList->OptimizeAndGenerate(_scriptingData, _functionDecl);
                mSecondElement.mExpressionList->GetExpressions(parameters);
            }
            
            std::vector<WNFunction*> fillVector;;
            _scriptingData.mFunctionManager->GetScriptFunctions(fillVector, mFirstElement.mIdentifier, _functionDecl->mFileName);

            WN_INT32 fewestConversion = -1;
            WN_INT32 numConversion = 0;
            std::vector<WNFunction*>::iterator targetVal = fillVector.end();
            for(std::vector<WNFunction*>::iterator  i = fillVector.begin(); i != fillVector.end(); ++i) {
                if((*i)->mTypes.size() != parameters.size()) {
                    continue;
                }
                size_t conversionCt = 0;
                size_t param = 0;
                for(param = 0; param < parameters.size(); ++param) {
                    if (parameters[param].mExpr->GetType() == (*i)->mTypes[param]->mScriptType) {
                        continue;
                    }
                    WNGenerateLLVMCast castOp = _scriptingData.mTypeManager->GetLLVMCastGenerator((*i)->mTypes[param]->mScriptType, parameters[param].mExpr->GetType());
                    if(castOp != WN_NULL) {
                        conversionCt += 1;
                    } else {
                        break;
                    }
                }
                if (param != parameters.size()) {
                    continue;
                }
                if (fewestConversion < 0 || conversionCt < static_cast<size_t>(fewestConversion)) {
                    numConversion = 1;
                    fewestConversion = static_cast<WN_INT32>(conversionCt);
                    targetVal = i;
                } else if (fewestConversion == conversionCt) {
                    numConversion += 1;
                }
            }
            if (targetVal == fillVector.end()) {
                mFail = "Error could not find valid function\n";
                return(WN_NULL);
            } else if (numConversion > 1) {
                mFail = "Error found more than one applicable function\n";
                return(WN_NULL);
            } else if (fewestConversion > 0) {
                mWarning = "Warning, implicit casting in conversions\n";
            }
            
            WNFunction* func = *targetVal;
            
            for(size_t i = 0; i < func->mTypes.size(); ++i) {
                WNGenerateLLVMCast castOp = _scriptingData.mTypeManager->GetLLVMCastGenerator(func->mTypes[i]->mScriptType, parameters[i].mExpr->GetType());
                parameters[i].mLlvmVal = castOp(parameters[i].mLlvmVal, *builder);
            }

            std::vector<llvm::Value*> callParams;
            for(size_t i = 0; i < parameters.size(); ++i) {
                callParams.push_back(parameters[i].mLlvmVal);
            }

            llvm::Value* retVal;
            if (func->mExternal) {
                retVal = builder->CreateCall(func->mLlvmFunction, callParams, "nativecall");
            } else {
                size_t specialVal = (size_t)&(func->mFunctionPointer);
                llvm::Type* intPtrType = _scriptingData.mTypeManager->GetIntPtrType();
                llvm::Constant* llConst = llvm::ConstantInt::get(intPtrType, specialVal);
                llvm::Constant* llZero = llvm::ConstantInt::get(intPtrType, 0);
                llvm::Value* v = llvm::ConstantExpr::getIntToPtr(llConst, llvm::PointerType::getUnqual(intPtrType));
                llvm::Value* allocPos = builder->CreateAlloca(intPtrType);
                llvm::Value* v2 = builder->CreateLoad(v, WN_TRUE, "GetFuncLoc");
                builder->CreateStore(v2, allocPos, WN_TRUE);
                llvm::BasicBlock* getFunctionBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "GetFunction");
                llvm::BasicBlock* runFunctionBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "RunFunction");
                builder->CreateCondBr(builder->CreateICmpEQ(llZero, v2), getFunctionBlock, runFunctionBlock);
                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(getFunctionBlock);
                builder->SetInsertPoint(getFunctionBlock);

                WNFunction* createFunc = _scriptingData.mFunctionManager->GetCreateFunctionFunction();
                std::vector<llvm::Value*> vals;
                llvm::Value* sEngine = llvm::ConstantInt::get(intPtrType, reinterpret_cast<size_t>(_scriptingData.mScriptingEngine));
                sEngine = builder->CreateIntToPtr(sEngine, llvm::PointerType::getUnqual(intPtrType), "");
                vals.push_back(sEngine);
                llvm::Value* funcPtr = llvm::ConstantInt::get(intPtrType, reinterpret_cast<size_t>(func));
                funcPtr = builder->CreateIntToPtr(funcPtr, llvm::PointerType::getUnqual(intPtrType), "");
                vals.push_back(funcPtr);
                llvm::Value* val = builder->CreateCall(createFunc->mLlvmFunction, vals, "CreateFunction");
                val = builder->CreatePtrToInt(val, intPtrType, "");
                builder->CreateStore(val, allocPos, "storeNewFuncPtr");
                builder->CreateBr(runFunctionBlock);
                _functionDecl->mLLVMFunction->getBasicBlockList().push_back(runFunctionBlock);
                builder->SetInsertPoint(runFunctionBlock);

                llvm::Value * actualFuncPtr = builder->CreateLoad(allocPos, WN_TRUE, "GetFunctionPointer");
                
                actualFuncPtr = builder->CreateIntToPtr(actualFuncPtr, llvm::PointerType::getUnqual(intPtrType), "");
                actualFuncPtr = builder->CreateBitCast(actualFuncPtr, func->mLlvmFunction->getType(), "BLARP");
                retVal = builder->CreateCall(actualFuncPtr, callParams, "WORP");
            }
            if(func->mStackRet) {
                llvm::Value* loc = builder->CreateAlloca(llvm::IntegerType::getInt8Ty(llvm::getGlobalContext()), retVal);
                
                WNFunction* tempfunc = _scriptingData.mFunctionManager->GetTemporaryStackFunction();
                std::vector<llvm::Value*> args;
                args.push_back(llvm::ConstantInt::get(_scriptingData.mTypeManager->GetIntPtrType(), 0));
                llvm::Value* copyLoc = builder->CreateCall(tempfunc->mLlvmFunction, args);
                args.clear();
                llvm::Type* llvmChar = _scriptingData.mTypeManager->GetLLVMType(_scriptingData.mTypeManager->GetType(eWNTypeChar));
                std::vector<llvm::Type*> memcpyTypes;

                memcpyTypes.push_back(llvm::PointerType::getUnqual(llvmChar));
                memcpyTypes.push_back(llvm::PointerType::getUnqual(llvmChar));
                memcpyTypes.push_back(llvm::IntegerType::getInt32Ty(llvm::getGlobalContext()));

                llvm::Function* fnc = llvm::Intrinsic::getDeclaration(_scriptingData.mModule, llvm::Intrinsic::memcpy, memcpyTypes);
                loc = builder->CreatePointerCast(loc, llvm::PointerType::getUnqual(llvmChar));
                copyLoc = builder->CreatePointerCast(copyLoc, llvm::PointerType::getUnqual(llvmChar));
                args.push_back(loc);
                args.push_back(copyLoc);
                args.push_back(retVal);
                args.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 0));
                args.push_back(llvm::ConstantInt::get(llvm::Type::getInt1Ty(llvm::getGlobalContext()), 0));
                builder->CreateCall(fnc, args, "");

                retVal = builder->CreatePointerCast(loc, _scriptingData.mTypeManager->GetLLVMType(func->mReturnType->mScriptType));
            }

            mElementType = func->mReturnType->mScriptType;
            return(retVal);
        }
        break;
        case eWNStructAccess: {
            llvm::Value * str = mFirstElement.mLHS->OptimizeAndGenerate(_scriptingData, _functionDecl);

            if(!_scriptingData.mTypeManager->IsStructType(mFirstElement.mLHS->GetType())) {
                mFail = "Error this is not a valid struct type\n";
                return(WN_NULL);
            }
            
            WN_SIZE_T loc;
            WNTypeElement typeElem;
            WNTypeElement structType = mFirstElement.mLHS->GetType();
            if(!_scriptingData.mTypeManager->GetStructValueLocation(structType, mSecondElement.mIdentifier, typeElem, loc)) {
                mFail = "Identifier does not exist\n";
                return(WN_NULL);
            }
            
            llvm::Type* type = llvm::PointerType::getUnqual(_scriptingData.mTypeManager->GetLLVMType(structType));

            llvm::Value* gepValues[2] = {llvm::ConstantInt::get(builder->getInt32Ty(), 0), llvm::ConstantInt::get(builder->getInt32Ty(), loc)};
#ifdef _WN_ANDROID
            llvm::Value* valLoc = builder->CreateGEP(str, &gepValues[0], &gepValues[1]);
#else
            llvm::Value* valLoc = builder->CreateGEP(str, llvm::makeArrayRef(gepValues, 2));
#endif

            mElementType = typeElem;
            return(builder->CreateLoad(valLoc));
        }
        break;
        case eWNReferenceCreation: {
            std::vector<llvm::Value*> val;
            for(size_t i = 0; i < mSecondElement.mArrayExpressions->size(); ++i){
                WNExpression* expr = (*mSecondElement.mArrayExpressions)[i];
                llvm::Value* t = expr->OptimizeAndGenerate(_scriptingData, _functionDecl);
                if (!t) {
                    mFail = "Error expression is invalid";
                    return(WN_NULL);
                }
                WNGenerateLLVMCast castOp = _scriptingData.mTypeManager->GetLLVMCastGenerator(_scriptingData.mTypeManager->GetType(eWNTypeInt), expr->GetType());
                if(!castOp) {
                    mFail = "Error cannot cast type to integer for lookup";
                }
                t = castOp(t, *builder);
                val.push_back(t);
            }
            mFirstElement.mDataInitializer->OptimizeAndGenerate(_scriptingData, _functionDecl);

            WNTypeElement e = mFirstElement.mDataInitializer->GetType();
            if(e == WNInvalidType) {
                mFail = "Cannot use invalid type as initializer";
                return(WN_NULL);
            }
            
            llvm::Value* retVal = _scriptingData.mTypeManager->CreateAllocation(_scriptingData, e, val, mHeapAlloc, mSuppressLastCreation);
           
            for(WN_SIZE_T i = 0; i < mSecondElement.mArrayExpressions->size(); ++i) {
                e = _scriptingData.mTypeManager->GetArrayType(e);
            }
            mElementType = e;
            return(retVal);
        }
        break;
        case eWNConstantStringInitialization: {
            WN_SIZE_T cnt = WNStrLen(mFirstElement.mStringInitializer);
            llvm::Type* gepType = llvm::IntegerType::getInt32Ty(llvm::getGlobalContext());
            WNTypeElement charType = _scriptingData.mTypeManager->GetType(eWNTypeChar);
            llvm::Type* llvmChar = _scriptingData.mTypeManager->GetLLVMType(charType);
            std::vector<llvm::Constant*> constArray;

            for(WN_SIZE_T i = 0; i < cnt; ++i) {
                constArray.push_back(llvm::ConstantInt::get(llvmChar, mFirstElement.mStringInitializer[i]));
            }
            llvm::ArrayType* type = llvm::ArrayType::get(llvmChar, cnt);
            llvm::Constant* init = llvm::ConstantArray::get(type, constArray);

           
            WN_CHAR stringplace[25];
            WN_SPRINTF(stringplace, "cstr%zd", _scriptingData.mTypeManager->mGlobalVariables.size());
            _scriptingData.mTypeManager->mGlobalVariables.push_back(new llvm::GlobalVariable(*_scriptingData.mModule, type, true, llvm::GlobalValue::ExternalLinkage, init, stringplace));
            
            std::vector<llvm::Value*> amounts;
            amounts.push_back(llvm::ConstantInt::get(_scriptingData.mTypeManager->GetIntPtrType(), cnt));
            llvm::Value* retVal = _scriptingData.mTypeManager->CreateAllocation(_scriptingData, charType, amounts, mHeapAlloc, false);
            WN_INT32 typeOffset = WNLLVMGetTypesPerType(_scriptingData, _scriptingData.mTypeManager->GetIntPtrType(), llvmChar);
            llvm::Value* stringLoc = builder->CreateGEP(retVal, llvm::ConstantInt::get(builder->getInt32Ty(), typeOffset));
            
            llvm::GlobalVariable* g = _scriptingData.mTypeManager->mGlobalVariables.back();

            llvm::Value* geps[2] = { llvm::ConstantInt::get(gepType, 0), llvm::ConstantInt::get(gepType, 0) };
#ifdef _WN_ANDROID
            llvm::Value* constLoc = builder->CreateGEP(g, &geps[0], &geps[1]);
#else
            llvm::Value* constLoc = builder->CreateGEP(g, llvm::makeArrayRef(geps, 2));
#endif
            std::vector<llvm::Type*> memcpyTypes;
            
            memcpyTypes.push_back(llvm::PointerType::getUnqual(llvmChar));
            memcpyTypes.push_back(llvm::PointerType::getUnqual(llvmChar));
            memcpyTypes.push_back(llvm::IntegerType::getInt32Ty(llvm::getGlobalContext()));

            llvm::Function* fnc = llvm::Intrinsic::getDeclaration(_scriptingData.mModule, llvm::Intrinsic::memcpy, memcpyTypes);
            std::vector<llvm::Value*> args;
            args.push_back(stringLoc);
            args.push_back(constLoc);
            args.push_back(llvm::ConstantInt::get(_scriptingData.mTypeManager->GetIntPtrType(), cnt * WNLLVMGetTypeSize(_scriptingData, llvmChar)));
            args.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 0));
            args.push_back(llvm::ConstantInt::get(llvm::Type::getInt1Ty(llvm::getGlobalContext()), 0));
            builder->CreateCall(fnc, args, "");

            mElementType = _scriptingData.mTypeManager->GetArrayType(_scriptingData.mTypeManager->GetType(eWNTypeChar));
            
            return(retVal);
        }
        break;
        case eWNArrayLength: {
            llvm::Value * array = mFirstElement.mLHS->OptimizeAndGenerate(_scriptingData, _functionDecl);
            if(!array) {
                return(WN_NULL);
            }
            if(!_scriptingData.mTypeManager->IsArrayType(mFirstElement.mLHS->GetType())) {
                mFail = "Error, cannot get the length of a non-array type";
                return(WN_NULL);
            }
            llvm::Type* ptrType = llvm::PointerType::getUnqual(_scriptingData.mTypeManager->GetLLVMType(_scriptingData.mTypeManager->GetType(eWNTypeInt)));
            array = builder->CreatePointerCast(array, ptrType);
            mElementType = _scriptingData.mTypeManager->GetType(eWNTypeInt);
            return(builder->CreateLoad(array));

        }
        break;
    }
    return(WN_NULL);
}

WNTypeElement WNExpression::GetType() {
    if (eWNConstantExpression == mType) {
        return(mElementType);
    }

    if (!mOptimized) {
        mFail = "You cannot properly determine type without optimization";
    }
    return(mElementType);
}

WN_BOOL WNExpression::ReportErrors() {
    WN_BOOL retVal = WNParseNode::ReportErrors();
    switch(mType) {
        case eWNArithmeticSquareBrackets:
        case eWNArithmeticAdd:
        case eWNArithmeticSubtract:
        case eWNArithmeticMultiply:
        case eWNArithmeticMod:
        case eWNArithmeticDivide:
        case eWNArithmeticEquality:
            retVal &= mFirstElement.mLHS->ReportErrors();
            retVal &= mSecondElement.mRHS->ReportErrors();
            break;
        case eWNReferenceTypeInitialization:
            retVal &= mFirstElement.mDataInitializer->ReportErrors();
            break;
    }
    return(retVal);
}

#ifdef _WN_MSVC
    #pragma warning(pop)
#endif
